import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'triangle_painter.dart';

const double _kMenuScreenPadding = 8.0;

class PopupMenu extends StatefulWidget {
  static void defalutChildOnTap() {}

  PopupMenu(
      {Key? key,
      @required this.onValueChanged,
      @required this.actions,
      @required this.child,
      this.childOnTap = defalutChildOnTap,
      this.pressType = PressType.longPress,
      this.pageMaxChildCount = 2,
      this.backgroundColor = Colors.black,
      this.direction = Axis.horizontal,
      this.menuWidth = 100,
      this.menuHeight = 30,
      this.alignment = Alignment.center,
      this.gradient});

  final ValueChanged<int>? onValueChanged;
  final List<Widget>? actions;
  final Widget? child;
  final PressType? pressType; // 点击方式 长按 还是单击
  final int? pageMaxChildCount;
  final Color? backgroundColor;
  final double? menuWidth;
  final double? menuHeight;
  final Axis? direction;
  final VoidCallback? childOnTap;
  final Alignment? alignment;
  final Gradient? gradient;

  @override
  _PopupMenuState createState() => _PopupMenuState();
}

class _PopupMenuState extends State<PopupMenu> {
  double? width;
  double? height;
  RenderBox? button;
  RenderBox? overlay;
  OverlayEntry? entry;

  @override
  void initState() {
    super.initState();

    WidgetsBinding.instance.addPostFrameCallback((call) {
      width = context.size!.width;
      height = context.size!.height;
      button = context.findRenderObject() as RenderBox;
      overlay = Overlay.of(context).context.findRenderObject() as RenderBox;
    });
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    removeOverlay();
  }

  @override
  void dispose() {
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () {
        widget.childOnTap!();
        removeOverlay();
        return Future.value(true);
      },
      child: GestureDetector(
        behavior: HitTestBehavior.translucent,
        child: widget.child,
        onTap: () {
          if (widget.pressType == PressType.singleClick) {
            widget.childOnTap!();
            onTap();
          }
        },
        onLongPress: () {
          if (widget.pressType == PressType.longPress) {
            widget.childOnTap!();
            onTap();
          }
        },
      ),
    );
  }

  void onTap() {
    Widget menuWidget = _MenuPopWidget(
        context,
        height,
        width,
        widget.actions,
        widget.pageMaxChildCount,
        widget.backgroundColor,
        widget.menuWidth,
        widget.menuHeight,
        button,
        overlay, (index) {
      if (index != -1) widget.onValueChanged!(index);
      removeOverlay();
    }, widget.child, widget.direction, widget.alignment, widget.gradient);

    entry = OverlayEntry(builder: (context) {
      return menuWidget;
    });
    Overlay.of(context).insert(entry!);
  }

  void removeOverlay() {
    if (entry != null) {
      entry!.remove();
      entry = null;
      widget.childOnTap!();
    }
  }
}

enum PressType {
  // 长按
  longPress,
  // 单击
  singleClick,
}

class _MenuPopWidget extends StatefulWidget {
  final BuildContext? btnContext;
  final List<Widget>? actions;
  final int? _pageMaxChildCount;
  final Color? backgroundColor;
  final double? menuWidth;
  final double? menuHeight;
  final double? _height;
  final double? _width;
  final RenderBox? button;
  final RenderBox? overlay;
  final Widget? carrier;
  final ValueChanged<int>? onValueChanged;
  final Axis? direction;
  final Alignment? alignment;
  final Gradient? gradient;

  _MenuPopWidget(
      this.btnContext,
      this._height,
      this._width,
      this.actions,
      this._pageMaxChildCount,
      this.backgroundColor,
      this.menuWidth,
      this.menuHeight,
      this.button,
      this.overlay,
      this.onValueChanged,
      this.carrier,
      this.direction,
      this.alignment,
      this.gradient);

  @override
  _MenuPopWidgetState createState() => _MenuPopWidgetState();
}

class _MenuPopWidgetState extends State<_MenuPopWidget> {
  int _curPage = 0;
  final double _arrowWidth = 40;
  final double _separatorWidth = 1;

  RelativeRect? position;

  @override
  void initState() {
    super.initState();
    position = RelativeRect.fromRect(
      Rect.fromPoints(
        widget.button!.localToGlobal(Offset.zero, ancestor: widget.overlay),
        widget.button!.localToGlobal(Offset.zero, ancestor: widget.overlay),
      ),
      Offset.zero & widget.overlay!.size,
    );
/* 
    position = RelativeRect.fromLTRB(
        context.size.width, widget.button.size.height, 10, 10); */
    // 那个 啥的位置
  }

  @override
  Widget build(BuildContext context) {
    // 这里计算出来 当前页的 child 一共有多少个
    int? _curPageChildCount =
        (_curPage + 1) * widget._pageMaxChildCount! > widget.actions!.length
            ? widget.actions!.length % widget._pageMaxChildCount!
            : widget._pageMaxChildCount;

    double _curArrowWidth = 0;
    int _curArrowCount = 0; // 一共几个箭头

    if (widget.actions!.length > widget._pageMaxChildCount!) {
      // 数据长度大于 widget._pageMaxChildCount
      if (_curPage == 0) {
        // 如果是第一页
        _curArrowWidth = _arrowWidth;
        _curArrowCount = 1;
      } else {
        // 如果不是第一页 则需要也显示左箭头
        _curArrowWidth = _arrowWidth * 2;
        _curArrowCount = 2;
      }
    }

    double _curPageWidth = widget.menuWidth! +
        (_curPageChildCount! - 1 + _curArrowCount) * _separatorWidth +
        _curArrowWidth;

    return GestureDetector(
      behavior: HitTestBehavior.opaque,
      onTap: () {
        widget.onValueChanged!(-1);
      },
      child: MediaQuery.removePadding(
        context: context,
        removeTop: true,
        removeBottom: true,
        removeLeft: true,
        removeRight: true,
        child: Builder(
          builder: (BuildContext context) {
            return CustomSingleChildLayout(
              // 这里计算偏移量
              delegate: _PopupMenuRouteLayout(
                  position!,
                  widget.menuHeight! + widget.menuHeight! * 0.4,
                  Directionality.of(widget.btnContext!),
                  widget._width!,
                  widget._height!,
                  widget.menuWidth!,
                  widget.menuHeight!,
                  widget.actions!,
                  widget.direction!),
              child: widget.direction == Axis.horizontal
                  ? buildHorizontal(_curPageWidth, context, _curPageChildCount,
                      _curArrowWidth, _curArrowCount, widget.alignment!)
                  : buildVertical(
                      position!,
                      _curPageWidth,
                      context,
                      _curPageChildCount,
                      _curArrowWidth,
                      _curArrowCount,
                      widget.alignment!),
            );
          },
        ),
      ),
    );
  }

  Widget buildVertical(
      RelativeRect position,
      double _curPageWidth,
      BuildContext context,
      int _curPageChildCount,
      double _curArrowWidth,
      int _curArrowCount,
      Alignment alignment) {
    bool isInverted = true;
    if ((position.top +
            widget.menuHeight! * widget.actions!.length +
            widget.menuHeight! * 0.4 +
            widget._height!) >
        ScreenUtil().screenHeight) {
      isInverted = false;
    }
    return Container(
      height: widget.menuHeight! * widget.actions!.length +
          widget.menuHeight! * 0.4,
      width: widget.menuWidth,
      child: Material(
        color: Colors.transparent,
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            isInverted
                ? CustomPaint(
                    size: Size(_curPageWidth, widget.menuHeight! * 0.4),
                    painter: TrianglePainter(
                      color: widget.backgroundColor,
                      position: position,
                      isInverted: true,
                      size: widget.button!.size,
                      screenWidth: MediaQuery.of(context).size.width,
                    ),
                  )
                : Container(),
            Expanded(
                child: Container(
              decoration: BoxDecoration(
                color: widget.backgroundColor,
                gradient: widget.gradient == null
                    ? LinearGradient(
                        begin: Alignment.topCenter,
                        end: Alignment.bottomCenter,
                        colors: [
                            Colors.white.withOpacity(0.1),
                            Colors.black.withOpacity(0.3),
                            Colors.black.withOpacity(0.3),
                            Colors.white.withOpacity(0.1),
                          ],
                        stops: [
                            0,
                            .2,
                            .9,
                            1
                          ])
                    : widget.gradient,
              ),
              child: ListView.builder(
                shrinkWrap: true,
                physics: NeverScrollableScrollPhysics(),
                scrollDirection: Axis.vertical,
                itemCount: widget.actions!.length,
                itemBuilder: (BuildContext context, int index) {
                  return Container(
                      height: widget.menuHeight,
                      child: ElevatedButton(
                        style: ButtonStyle(
                          padding: MaterialStateProperty.all(EdgeInsets.zero),
                          backgroundColor:
                              MaterialStateProperty.resolveWith((states) {
                            //设置按下时的背景颜色
                            if (states.contains(MaterialState.pressed)) {
                              return Colors.orange[200];
                            }
                            //默认不使用背景颜色
                            return Colors.white.withOpacity(0);
                          }),
                          elevation: MaterialStateProperty.all(0),
                        ),
                        child: Container(
                          alignment: alignment,
                          child: widget.actions![
                              _curPage * widget._pageMaxChildCount! + index],
                        ),
                        onPressed: () {
                          widget.onValueChanged!(
                              _curPage * widget._pageMaxChildCount! + index);
                        },
                      ));
                },
              ),
            )),
            isInverted
                ? Container()
                : CustomPaint(
                    size: Size(_curPageWidth, widget.menuHeight! * 0.4),
                    painter: TrianglePainter(
                      color: widget.backgroundColor,
                      position: position,
                      size: widget.button!.size,
                      screenWidth: MediaQuery.of(context).size.width,
                    ),
                  ),
          ],
        ),
      ),
    );
  }

  SizedBox buildHorizontal(
      double _curPageWidth,
      BuildContext context,
      int _curPageChildCount,
      double _curArrowWidth,
      int _curArrowCount,
      Alignment alignment) {
    bool isInverted = true;
    if ((position!.top +
            widget.menuHeight! +
            widget.menuHeight! * 0.4 +
            widget._height!) >
        ScreenUtil().screenHeight) {
      isInverted = false;
    }
    return SizedBox(
      height: widget.menuHeight! + widget.menuHeight! * 0.4,
      width: _curPageWidth,
      child: Material(
        color: Colors.transparent,
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            isInverted
                ? CustomPaint(
                    size: Size(_curPageWidth, widget.menuHeight! * 0.4),
                    painter: TrianglePainter(
                      color: widget.backgroundColor,
                      position: position,
                      isInverted: true,
                      size: widget.button!.size,
                      screenWidth: MediaQuery.of(context).size.width,
                    ),
                  )
                : Container(),
            Expanded(
              child: Stack(
                children: <Widget>[
                  ClipRRect(
                    borderRadius: BorderRadius.all(Radius.circular(5)),
                    child: Container(
                      color: widget.backgroundColor!.withAlpha(200),
                      height: widget.menuHeight,
                    ),
                  ),
                  Row(
                    mainAxisSize: MainAxisSize.min,
                    children: <Widget>[
                      // 左箭头：判断是否是第一页，如果是第一页则不显示
                      _curPage == 0
                          ? Container(
                              height: widget.menuHeight,
                            )
                          : InkWell(
                              onTap: () {
                                setState(() {
                                  _curPage--;
                                });
                              },
                              child: Container(
                                width: _arrowWidth,
                                height: widget.menuHeight,
                                child: Image.asset(
                                  'images/util/left_white.png',
                                  fit: BoxFit.none,
                                ),
                              ),
                            ),
                      // 左箭头：判断是否是第一页，如果是第一页则不显示
                      _curPage == 0
                          ? Container(
                              height: widget.menuHeight,
                            )
                          : Container(
                              width: 1,
                              height: widget.menuHeight,
                              color: Colors.grey,
                            ),

                      // 中间是ListView
                      _buildList(_curPageChildCount, _curPageWidth,
                          _curArrowWidth, _curArrowCount),

                      // 右箭头：判断是否有箭头，如果有就显示，没有就不显示
                      _curArrowCount > 0
                          ? Container(
                              width: 1,
                              color: Colors.grey,
                              height: widget.menuHeight,
                            )
                          : Container(
                              height: widget.menuHeight,
                            ),
                      _curArrowCount > 0
                          ? InkWell(
                              onTap: () {
                                if ((_curPage + 1) *
                                        widget._pageMaxChildCount! <
                                    widget.actions!.length)
                                  setState(() {
                                    _curPage++;
                                  });
                              },
                              child: Container(
                                width: _arrowWidth,
                                height: widget.menuHeight,
                                child: Image.asset(
                                  (_curPage + 1) * widget._pageMaxChildCount! >=
                                          widget.actions!.length
                                      ? 'images/util/right_gray.png'
                                      : 'images/util/right_white.png',
                                  fit: BoxFit.none,
                                ),
                              ),
                            )
                          : Container(
                              height: widget.menuHeight,
                            ),
                    ],
                  ),
                ],
              ),
            ),
            isInverted
                ? Container()
                : CustomPaint(
                    size: Size(_curPageWidth, widget.menuHeight! * 0.4),
                    painter: TrianglePainter(
                      color: widget.backgroundColor,
                      position: position,
                      size: widget.button!.size,
                      screenWidth: MediaQuery.of(context).size.width,
                    ),
                  ),
          ],
        ),
      ),
    );
  }

  Widget _buildList(int _curPageChildCount, double _curPageWidth,
      double _curArrowWidth, int _curArrowCount) {
    return ListView.separated(
      shrinkWrap: true,
      physics: NeverScrollableScrollPhysics(),
      scrollDirection: Axis.horizontal,
      itemCount: _curPageChildCount,
      itemBuilder: (BuildContext context, int index) {
        return GestureDetector(
          onTap: () {
            widget
                .onValueChanged!(_curPage * widget._pageMaxChildCount! + index);
          },
          child: SizedBox(
            width: (_curPageWidth -
                    _curArrowWidth -
                    (_curPageChildCount - 1 + _curArrowCount) *
                        _separatorWidth) /
                _curPageChildCount,
            height: widget.menuHeight,
            child: Center(
              child: widget
                  .actions![_curPage * widget._pageMaxChildCount! + index],
            ),
          ),
        );
      },
      separatorBuilder: (BuildContext context, int index) {
        return Container(
          width: 1,
          height: widget.menuHeight,
          color: Colors.grey,
        );
      },
    );
  }
}

// Positioning of the menu on the screen.
class _PopupMenuRouteLayout extends SingleChildLayoutDelegate {
  _PopupMenuRouteLayout(
      this.position,
      this.selectedItemOffset,
      this.textDirection,
      this.width,
      this.height,
      this.menuWidth,
      this.menuHeight,
      this.actions,
      this.direction);

  // Rectangle of underlying button, relative to the overlay's dimensions.
  final RelativeRect position;

  // The distance from the top of the menu to the middle of selected item.
  //
  // This will be null if there's no item to position in this way.
  final double selectedItemOffset;

  // Whether to prefer going to the left or to the right.
  final TextDirection textDirection;
  // 附着元素 本身宽高
  final double width;
  final double height;
  // 菜单的宽高
  final double menuWidth;
  final double menuHeight;
  final List<Widget> actions;
  final Axis direction;
  // We put the child wherever position specifies, so long as it will fit within
  // the specified parent size padded (inset) by 8. If necessary, we adjust the
  // child's position so that it fits.

  @override
  BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
    // The menu can be at most the size of the overlay minus 8.0 pixels in each
    // direction.     -  Offset(_kMenuScreenPadding * 2.0, _kMenuScreenPadding * 2.0)
    return BoxConstraints.loose(constraints.biggest);
  }

  @override
  Offset getPositionForChild(Size size, Size childSize) {
    // size: The size of the overlay.
    // childSize: The size of the menu, when fully open, as determined by
    // getConstraintsForChild.

    // Find the ideal vertical position.
    double y;
    y = position.top;
    double x;

    // 如果menu 的宽度 小于 child 的宽度，则直接把menu 放在 child 中间
    if (childSize.width < width) {
      x = position.left + (width - childSize.width) / 2;
    } else {
      // 如果靠右
      if (position.left > size.width - (position.left + width)) {
        if (size.width - (position.left + width) >
            childSize.width / 2 + _kMenuScreenPadding) {
          x = position.left - (childSize.width - width) / 2;
        } else {
          x = position.left + width - childSize.width;
        }
      } else if (position.left < size.width - (position.left + width)) {
        if (position.left > childSize.width / 2 + _kMenuScreenPadding) {
          x = position.left - (childSize.width - width) / 2;
        } else
          x = position.left;
      } else {
        x = position.right - width / 2 - childSize.width / 2;
      }
    }
    if (direction == Axis.vertical) {
      if (y + menuHeight * actions.length + height > size.height) {
        y = position.top - menuHeight * actions.length - menuHeight * 0.4;
      } else {
        y = position.top + height;
      }
    } else {
      if (y + menuHeight + height > size.height) {
        y = position.top - height - menuHeight * 0.4;
      } else {
        y = position.top + height;
      }
    }

    return Offset(x, y);
  }

  @override
  bool shouldRelayout(_PopupMenuRouteLayout oldDelegate) {
    return position != oldDelegate.position;
  }
}
